package br.org.acessobrasil.processoacessibilidade.bo;



import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import br.org.acessobrasil.ases.regras.RegrasHardCodedEmag;
import br.org.acessobrasil.io.G_File;
import br.org.acessobrasil.padrao.PadraoNumerico;
import br.org.acessobrasil.persistencia.DataSource;
import br.org.acessobrasil.processoacessibilidade.dao.ErroOuAvisoDao;
import br.org.acessobrasil.processoacessibilidade.dao.ErrosRegraDao;
import br.org.acessobrasil.processoacessibilidade.dao.LinguagemProgramacaoDao;
import br.org.acessobrasil.processoacessibilidade.dao.PaginaDao;
import br.org.acessobrasil.processoacessibilidade.dao.RelatorioDao;
import br.org.acessobrasil.processoacessibilidade.dao.SitioDao;
import br.org.acessobrasil.processoacessibilidade.vo.ErroOuAvisoPro;
import br.org.acessobrasil.processoacessibilidade.vo.ErrosRegraPro;
import br.org.acessobrasil.processoacessibilidade.vo.LinguagemProgramacaoPro;
import br.org.acessobrasil.processoacessibilidade.vo.PaginaPro;
import br.org.acessobrasil.processoacessibilidade.vo.RelatorioAcessibilidadePro;
import br.org.acessobrasil.processoacessibilidade.vo.SitioPro;

/**
 * Responsavel por emitir relatorios
 * @author Fabio Issamu Oshiro, Jonatas Pacheco Ribeiro
 *
 */
public class Relator {
	private static DataSource dataSource;
	private static ErroOuAvisoDao erroOuAvisoDao;
	private static SitioDao sitioDao;
	private static ErrosRegraDao errosRegraDao;
	private static PaginaDao paginaDao;
	private static RelatorioDao relatorioDao;
	private static LinguagemProgramacaoDao linguagemProgramacaoDao;
	
	private static Logger logger = Logger.getLogger(Relator.class);
	
	public void gerarRelatorio(SitioPro sitio) throws Exception {
		if(sitio.getId()==0l){
			SitioPro sitioDB = sitioDao.find(sitio.getUrlInicial());
			if(sitioDB==null){
				throw new NullPointerException("sitio '" + sitio.getUrlInicial() + "' not found.");
			}else{
				sitio = sitioDB;
			}
		}
		
		Date ultimaData = relatorioDao.getUltimaData(sitio);
		
		if(ultimaData==null){
			ultimaData = new Date();//throw new Exception("Nao existe data de relatorio atual para o sitio " + sitio.getId());
		}
		gerarRelatorio(sitio, ultimaData);
	}
	public void gerarRelatorio(SitioPro sitio, String strDataGerada) throws Exception {
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date dataGerada = simpleDateFormat.parse(strDataGerada);
		gerarRelatorio(sitio, dataGerada);
	}
	public void gerarRelatorio(SitioPro sitio,Date dataGerada) throws Exception {
		
		String url = sitio.getUrlInicial();
		if(sitio.getId()==0l){
			sitio = sitioDao.find(sitio.getUrlInicial());
		}
		if(sitio==null) throw new Exception("SitioPro nao encontrado "+url);
		
		//Especificar a pasta onde sera salvo
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss");
		String pastaData = simpleDateFormat.format(dataGerada);
		String basePath = Arquivista.pastaDeArquivosDoSitio+"/relatorios/"+pastaData+"/";
		gerarRelatorioGeral(sitio,basePath,dataGerada);
	}
	
	private String gerarRelatorioLinguagem(SitioPro sitio){
		String retorno = "Linguagens utilizadas:\n";
		List<LinguagemProgramacaoPro> linguagens = linguagemProgramacaoDao.list();
		for(LinguagemProgramacaoPro lingua:linguagens){
			int total = paginaDao.count(lingua,sitio);
			retorno +="\t" + total + "= \t" + lingua.getNomeLinguagem() + "\n";
		}
		return retorno;
	}
	private void gerarRelatorioResumoPaginaErros(List<RelatorioAcessibilidadePro> listaRelatorio,String basePath){
		logger.info( "Gerando resumo, indexado por pagina");
		G_File arq = new G_File(basePath+"/resumoPorPagina.html");
		HashSet<Long> incluido = new HashSet<Long>();
		arq.write("Resumo");
		arq.append("<table border='1'>");
		arq.append("<tr>");
		arq.append("<th>Url</th>");
		arq.append("<th>A1 </th>");
		arq.append("<th>E1 </th>");
		arq.append("<th>A2 </th>");
		arq.append("<th>E2 </th>");
		arq.append("<th>A3 </th>");
		arq.append("<th>E3 </th>");
		arq.append("<th>HTML</th>");
		arq.append("<th>TE</th>");
		arq.append("<th>TA</th>");
		arq.append("</tr>");
		int tot = listaRelatorio.size();
		for(int i=0;i<tot;i++){
			RelatorioAcessibilidadePro relatorio = listaRelatorio.get(i);
			if(incluido.contains(relatorio.getPagina().getIdPagina())) continue;
			incluido.add(relatorio.getPagina().getIdPagina());
			arq.append("<tr>");
			arq.append("<td>" + relatorio.getPagina().getUrl() + "</td>");
			arq.append("<td>" + relatorio.getListAvisoP1().size() + "</td>");
			arq.append("<td>" + relatorio.getListErroP1().size() + "</td>");
			arq.append("<td>" + relatorio.getListAvisoP2().size() + "</td>");
			arq.append("<td>" + relatorio.getListErroP2().size() + "</td>");
			arq.append("<td>" + relatorio.getListAvisoP3().size() + "</td>");
			arq.append("<td>" + relatorio.getListErroP3().size() + "</td>");
			arq.append("<td>" + relatorio.getListErroXHTML().size() + "</td>");
			//total de erros
			arq.append("<td>" + (relatorio.getListErroP1().size() + relatorio.getListErroP2().size() + relatorio.getListErroP3().size()) + "</td>");
			arq.append("<td>" + (relatorio.getListAvisoP1().size() + relatorio.getListAvisoP2().size() + relatorio.getListAvisoP3().size()) + "</td>");
			arq.append("</tr>");
		}
		arq.append("</table>");
	}
	private void gerarRelatorioGeral(SitioPro sitio,String basePath,Date dataGerada) throws Exception{
	    logger.debug("Gerando relatorio do sitio "+sitio.getUrlInicial());
		
		logger.info("os relatorios serao gravados na pasta '"+basePath+"'");
		
		//Colocar o cabecalho
		String htmlRelatorio="";
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss dd/MM/yyyy");
		htmlRelatorio+="Relatorio de " + simpleDateFormat.format(dataGerada) +"\n";
		
		logger.info("Consultando os relatorios do banco de dados");
		List<RelatorioAcessibilidadePro> listaRelatorio = relatorioDao.listarRelatorios(sitio, dataGerada);
		
		gerarRelatorioResumoPaginaErros(listaRelatorio,basePath);
		
		//Condensar os erros
		logger.info("Condensando os erros de html");
		
		int totIdErroHtml=0;
		//gera um id por descricao de erro
		HashMap<String,Integer> erroHtml2Id = new HashMap<String,Integer>(300);  
		HashMap<Integer,String> relatorioPorErroHtml = new HashMap<Integer,String>();
		HashMap<Integer,HashSet<Long>> relatorioPorErroHtmlPaginaExistente = new HashMap<Integer,HashSet<Long>>();
		String relatorioHtmlGeral = "";
		int tot = listaRelatorio.size();
		for(int i=0;i<tot;i++){
			RelatorioAcessibilidadePro relatorio = listaRelatorio.get(i);
			for(ErrosRegraPro erroRegraHtml:relatorio.getListErroXHTML()){
				Integer idErroHtml = erroHtml2Id.get(erroRegraHtml.getTexto());
				String link = "<a href=\"../../"+PadraoNumerico.completarZeros(relatorio.getPagina().getIdPagina(),5)+".html\">"+PadraoNumerico.completarZeros(relatorio.getPagina().getIdPagina(),5)+"</a><br />"; 
				if(idErroHtml==null){
					//erro novo
					totIdErroHtml++;
					erroHtml2Id.put(erroRegraHtml.getTexto(), totIdErroHtml);
					HashSet<Long> contemPagina = new HashSet<Long>();
					contemPagina.add(relatorio.getPagina().getIdPagina());
					relatorioPorErroHtmlPaginaExistente.put(totIdErroHtml,contemPagina);
					relatorioPorErroHtml.put(totIdErroHtml, link);
					relatorioHtmlGeral+="<a href=\"erroHtml_"+totIdErroHtml+".html\">"+erroRegraHtml.getTexto()+"</a><hr />";
				}else{
					//erro existente
					//verificar se a pagina esta listada
					if(!relatorioPorErroHtmlPaginaExistente.get(idErroHtml).contains(relatorio.getPagina().getIdPagina())){
						String temp = relatorioPorErroHtml.get(idErroHtml)+link;
						relatorioPorErroHtml.put(idErroHtml, temp);
					}
				}
			}
		}
		G_File arqRelatorioHtmlGeral = new G_File(basePath+"defaultHtmlGeral.html");
		arqRelatorioHtmlGeral.write(relatorioHtmlGeral);
		
		ArrayList<Integer> listKey = new ArrayList<Integer>(relatorioPorErroHtml.keySet());
		for(Integer key:listKey){
			G_File arq = new G_File(basePath+"erroHtml_"+key+".html");
			arq.write(relatorioPorErroHtml.get(key));
		}
		
		logger.info("Consultando erros p1");
		List<ErroOuAvisoPro> listaP1 = erroOuAvisoDao.getErrosP1Acessibilidade(sitio,dataGerada);
		htmlRelatorio+="Total p1 = " + listaP1.size()+"\n";
		logger.info("Consultando erros p2");
		List<ErroOuAvisoPro> listaP2 = erroOuAvisoDao.getErrosP2Acessibilidade(sitio,dataGerada);
		htmlRelatorio+="Total p2 = " + listaP2.size()+"\n";
		logger.info("Consultando erros p3");
		List<ErroOuAvisoPro> listaP3 = erroOuAvisoDao.getErrosP3Acessibilidade(sitio,dataGerada);
		htmlRelatorio+="Total p3 = " + listaP3.size()+"\n";
		htmlRelatorio+="Geral " + (listaP1.size() + listaP2.size()+listaP3.size()) +"\n";
		logger.info("Consultando erros Html");
		List<ErroOuAvisoPro> listaHtml = erroOuAvisoDao.getErrosHtml(sitio,dataGerada);
		htmlRelatorio+="Total html = <a href=\"defaultHtmlGeral.html\">" + listaHtml.size()+"</a>\n";
		
		logger.info("Gerando pagina com erros de html");
		
		
		
		HashMap<String,Integer> contaQtd = new HashMap<String,Integer>();
		for(ErroOuAvisoPro erro:listaP1){
			Integer res = contaQtd.get(erro.getCodTextoRegra());
			if(res==null){
				contaQtd.put(erro.getCodTextoRegra(), 1);
			}else{
				contaQtd.put(erro.getCodTextoRegra(), res+1);
			}
		}
		for(ErroOuAvisoPro erro:listaP2){
			Integer res = contaQtd.get(erro.getCodTextoRegra());
			if(res==null){
				contaQtd.put(erro.getCodTextoRegra(), 1);
			}else{
				contaQtd.put(erro.getCodTextoRegra(), res+1);
			}
		}
		
		for(ErroOuAvisoPro erro:listaP3){
			Integer res = contaQtd.get(erro.getCodTextoRegra());
			if(res==null){
				contaQtd.put(erro.getCodTextoRegra(), 1);
			}else{
				contaQtd.put(erro.getCodTextoRegra(), res+1);
			}
		}
		int paginasDistintas=0;
		HashSet<Long> pagListada = new HashSet<Long>(3000);
		List<String> mapKeys = new ArrayList<String>(contaQtd.keySet());
		Collections.sort(mapKeys);
		RegrasHardCodedEmag regras  = new RegrasHardCodedEmag();
		for(String key:mapKeys){
			logger.info("Consolidando erros de regra "+key);
			String erroDesc =key+": "+regras.getTextoRegra(key); 
			htmlRelatorio+=erroDesc + "\n\t<a href=\"erro"+key+".html\">" + contaQtd.get(key)+"</a>";
			List<PaginaPro> paginas = erroOuAvisoDao.getListPaginaComErro(key,sitio,dataGerada);
			String htmlListaPaginas = erroDesc + "<hr />";
			int n=0;
			for(PaginaPro pagina: paginas){
				if(!pagListada.contains(pagina.getIdPagina())){
					paginasDistintas++;
					pagListada.add(pagina.getIdPagina());
				}
				htmlListaPaginas += ++n + " - <a href=\"../../"+PadraoNumerico.completarZeros(pagina.getIdPagina(), 5)+".html\">";
				htmlListaPaginas += PadraoNumerico.completarZeros(pagina.getIdPagina(), 5) + "</a> &nbsp; <a href=\""+pagina.getUrl()+"\">Original '"+pagina.getUrl()+"'</a> <br />";
			}
			G_File erroX = new G_File(basePath+"erro"+key+".html");
			erroX.write(htmlListaPaginas);
			htmlRelatorio+=" distribu&iacute;dos em " + paginas.size() + " p&aacute;ginas\n";
		}
		htmlRelatorio+="Total de paginas distintas com erro "+paginasDistintas + "\n";
		htmlRelatorio+=gerarRelatorioLinguagem(sitio);
		G_File relatorio = new G_File(basePath+"default.html");
		relatorio.write("<pre>" + htmlRelatorio + "</pre>");
		
		logger.debug("Ok terminado");
	}
	
	public static void setSitioDao(SitioDao sitioDao) {
		Relator.sitioDao = sitioDao;
	}
	public static ErroOuAvisoDao getErroOuAvisoDao() {
		return erroOuAvisoDao;
	}
	public static void setErroOuAvisoDao(ErroOuAvisoDao erroOuAvisoDao) {
		Relator.erroOuAvisoDao = erroOuAvisoDao;
	}
	public static RelatorioDao getRelatorioDao() {
		return relatorioDao;
	}
	public static void setRelatorioDao(RelatorioDao relatorioDao) {
		Relator.relatorioDao = relatorioDao;
	}
	
	public static void setDataSource(DataSource dataSource) {
		Relator.dataSource = dataSource;
	}
	private static DataSource getDataSource(){
		if(dataSource==null){
			ApplicationContext ap = new ClassPathXmlApplicationContext("applicationContext.xml");
			dataSource = (DataSource)ap.getBean("dataSource");
		}
		return dataSource;
	}
	public static ErrosRegraDao getErrosRegraDao() {
		return errosRegraDao;
	}
	public static void setErrosRegraDao(ErrosRegraDao errosRegraDao) {
		Relator.errosRegraDao = errosRegraDao;
	}
	
	public static PaginaDao getPaginaDao() {
		return paginaDao;
	}
	public static void setPaginaDao(PaginaDao paginaDao) {
		Relator.paginaDao = paginaDao;
	}
	public static void setLinguagemProgramacaoDao(LinguagemProgramacaoDao linguagemProgramacaoDao) {
		Relator.linguagemProgramacaoDao = linguagemProgramacaoDao;
	}
}
